package core

import (
	"encoding/json"
	"fmt"
	"net"
	"sync"
	"time"

	"gitee.com/huanghua_2017/blade/comm"
	"gitee.com/huanghua_2017/blade/state"
	"gitee.com/huanghua_2017/blade/types"
	"github.com/gorilla/websocket"
	"golang.org/x/xerrors"
)

func (slf *Core) startSlave(wsconn *websocket.Conn, addr string) error {
	conns := slf.State.FindSlaveWSConn(addr)
	if conns != nil && conns.Vaild {
		return xerrors.New("ports cannot be mapped multiple times")
	}

	listener, err := net.Listen("tcp", "0.0.0.0:"+addr)
	if err != nil {
		return err
	}

	rlistener, err := net.Listen("tcp", "0.0.0.0:0")
	if err != nil {
		listener.Close()
		return err
	}

	res := types.HelloResStruct{}
	res.Address = fmt.Sprint(rlistener.Addr().(*net.TCPAddr).Port)
	wmsg, _ := json.Marshal(res)
	comm.WriteWSMessage(wsconn, wmsg)

	slf.Logger.Printf("slave 映射 %s 到 %s \n", addr, res.Address)

	sc := &state.SlaveConn{
		Vaild:       true,
		Dead:        false,
		Conn:        wsconn,
		Laddr:       addr,
		Resaddr:     res.Address,
		Listener:    listener,
		Reslistener: rlistener,
		Wschan:      make(chan []byte, 1000),
		Stoping:     make(chan struct{}, 5),
		Notify:      make(chan struct{}, 5),
		Begin:       time.Now(),
	}

	slf.State.UpdateSlaveWSConn(addr, sc)

	slf.slaveListen(sc)
	go slf.slaveExchange(sc)
	return nil
}

func (slf *Core) slaveListen(sc *state.SlaveConn) {
	wsaddr := sc.Conn.RemoteAddr().String()

	var once sync.Once
	exitfun := func() {
		sc.Vaild = false
		sc.Dead = true
		close(sc.Stoping)
		sc.Listener.Close()
		sc.Reslistener.Close()
		slf.State.RemoveSlaveWSConn(sc.Laddr)
	}

	go func() {
		for {
			select {
			case <-sc.Stoping:
				sc.Conn.Close()
				return
			default:
				sc.Conn.SetReadDeadline(time.Now().Add(time.Second * 5))
				_, msg, err := sc.Conn.ReadMessage()
				if err != nil {
					slf.Logger.Printf("slave 下游服务器断开连接: %s, %+v\n", wsaddr, err)
					once.Do(exitfun)
					return
				}
				l := len(msg)
				if l > 0 {
					msg = comm.DecodeMessage(msg)
					if msg[0] == '0' {
						sc.Wschan <- msg
					} else if msg[0] == '1' {
						once.Do(exitfun)
						slf.Logger.Printf("slave 客户端取消映射 %s, %s\n", sc.Laddr, sc.Resaddr)
						return
					}
				}
			}
		}
	}()

	go func() {
		for {
			select {
			case <-sc.Stoping:
				return
			case wmsg := <-sc.Wschan:
				err := comm.WriteWSMessage(sc.Conn, wmsg)
				if err != nil {
					slf.Logger.Printf("slave 发送数据失败: %s, %+v\n", wsaddr, err)
					once.Do(exitfun)
					return
				}
			}
		}
	}()
}

func (slf *Core) slaveExchange(sc *state.SlaveConn) {
	cch := make(chan net.Conn, 10)
	tmr := time.NewTicker(time.Second * 5).C

	slf.State.InsertSlaveConn(sc.Laddr, sc)
	defer slf.State.DeleteSlaveConn(sc.Laddr)

	for !sc.Dead {
		lconn, err := sc.Listener.Accept()
		if err != nil {
			continue
		}
		slf.Logger.Printf("slave Accept[1]  %s : %v\n", lconn.RemoteAddr().String(), err)

		sc.Wschan <- []byte("slave")

		go func() {
			rconn, err := sc.Reslistener.Accept()
			slf.Logger.Printf("slave Accept[2]  : %v\n", err)
			select {
			case cch <- rconn:
			case <-sc.Stoping:
				return
			}
		}()

		tmr = time.NewTicker(time.Second * 5).C
		var rconn net.Conn
		select {
		case <-sc.Stoping:
			return
		case <-tmr:
			slf.Logger.Printf("slave 等待客户端的连接超时，关闭 %s 到映射断开的连接\n", lconn.RemoteAddr().String())
			lconn.Close()
			continue
		case rconn = <-cch:
			if rconn == nil {
				lconn.Close()
				continue
			}
		}

		go func() {
			slf.Logger.Printf("slave %s -> %s 开始转发数据\n", lconn.RemoteAddr().String(), rconn.RemoteAddr().String())
			comm.TCPConnCopy(slf.Logger, lconn, rconn)
			slf.Logger.Println("slave 转发数据关闭")
		}()
	}
	slf.Logger.Printf("slave %s -> %s 退出数据交换协程\n", sc.Laddr, sc.Resaddr)
}
